package cn.itcast.nems.manager.activity;

import cn.itcast.ic.common.bean.CommonErrorInfo;
import cn.itcast.ic.common.bean.Response;
import cn.itcast.ic.common.exception.ExceptionProducer;
import cn.itcast.ic.common.exception.ICException;
import cn.itcast.nems.manager.activity.dto.BpmProcessResponseDTO;
import cn.itcast.nems.manager.util.CustomBeanUtil;
import com.alibaba.fastjson.JSON;
import com.itcast.activiti.sdk.config.Config;
import com.itcast.activiti.sdk.constant.AttachmentTypeConstant;
import com.itcast.activiti.sdk.domain.Attachment;
import com.itcast.activiti.sdk.domain.StartProcessInstance;
import com.itcast.activiti.sdk.domain.TaskStart;
import com.itcast.activiti.sdk.exception.ActivitiBusinessException;
import com.itcast.activiti.sdk.factory.Factory;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;


/**
 * <p>
 * 调用 activity work flow 接口
 * </p>
 *
 * @author zhouwei
 * @since 2023/11/7 10:35 AM
 **/
@Slf4j
@RequiredArgsConstructor
public class BpmWorkflowManager {
    /**
     * 调用工作流传输的 租户id
     */
    public static final String TENANT_ID = "NEMS";

    private final BpmProperties bpmProperties;

    /**
     * 创建流程
     *
     * @param createdBy            流程创建人
     * @param name                 流程名称
     * @param businessKey          业务key 唯一标识流程实例
     * @param processDefinitionKey 流程定义key
     * @param passbackParam        附加参数
     * @param variables            流程变量
     * @param msgLink              消息跳转链接
     * @param msgTypes             审批及知会消息提醒方式，可以允许多个
     */
    public BpmProcessResponseDTO startProcess(String createdBy, String name, String businessKey, String processDefinitionKey, String passbackParam, String msgLink, Map<String, Object> variables, List<SendMessageType> msgTypes) {
        if (StringUtils.hasText(msgLink)) {
            variables.put("msgLink", msgLink);
        }
        // 发送消息类型：多个以逗号分隔 如："cp_wechat_system,mail"
        // 1.企业微信系统消息 msgTypes（cp_wechat_system）
        // 2.邮件消息 msgTypes（mail）
        if (!msgTypes.isEmpty()) {
            List<String> collect = msgTypes.stream().map(SendMessageType::getCode).collect(Collectors.toList());
            variables.put("msgTypes", String.join(",", collect));
        }

        //创建工作流
        Factory.setOptions(getConfig());
        StartProcessInstance startProcessInstance = StartProcessInstance.builder()
                .userName(createdBy)
                .name(name)
                .tenantId(TENANT_ID)
                .businessKey(businessKey)
                .processDefinitionKey(processDefinitionKey)
                .passbackParam(passbackParam)
                .variables((HashMap<String, Object>) variables)
                .build();
        log.debug("启动流程请求参数 startProcessInstance : {}", JSON.toJSON(startProcessInstance));

        Response response = Factory.Process.processStartUserCompleteTaskApply(startProcessInstance);
        log.debug("启动流程响应数据 {}", JSON.toJSON(response));
        if (!response.isSuccess()) {
            throw new ActivitiBusinessException(response.getCode(), response.getMessage());
        }
        var map = (LinkedHashMap) response.getData();
        try {
            return CustomBeanUtil.mapToObject(map, BpmProcessResponseDTO.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 流程审批
     *
     * @param businessKey   业务唯一标识
     * @param operatorEmail 待办人账号
     * @return taskId 任务唯一标识
     */
    public String audit(String businessKey, String operatorEmail, TaskStart taskStart) {
        Factory.setOptions(getConfig());
        taskStart.setTenantId(TENANT_ID);
        log.info("流程审批请求参数 businessKey {},assignee {},taskStart : {}", businessKey, operatorEmail, JSON.toJSON(taskStart));
        Response response = Factory.Task.completeTaskApply(businessKey, operatorEmail, taskStart);
        log.info("流程审批响应数据 {}", JSON.toJSON(response));
        if (!response.isSuccess()) {
            throw new ICException(CommonErrorInfo.unkownError(response.getMessage()));
        }
        return String.valueOf(response.getData());
    }

    /**
     * 撤回指定凭单的审批流程
     *
     * @param businessKey 业务key
     */
    public Object cancel(String businessKey, String assignee) {
        Factory.setOptions(getConfig());
        log.info("撤回指定凭单的审批流程 businessKey {}", businessKey);
        var response = Factory.Task.cancel(TENANT_ID, businessKey, assignee, "撤回");
        log.info("撤回指定凭单的审批流程 {}", JSON.toJSON(response));
        if (!response.isSuccess()) {
            throw new ActivitiBusinessException(response.getCode(), response.getMessage());
        }
        return response.getData();
    }

    /**
     * 流程激活
     * @param businessKey
     * @return
     */
    public Object processActivate(String businessKey) {
        Factory.setOptions(getConfig());
        var response = Factory.Process.processActivate(businessKey, TENANT_ID);
        if (!response.isSuccess()) {
            throw new ActivitiBusinessException(response.getCode(), response.getMessage());
        }
        return response.getData();
    }

    /**
     * 获取指定审批流程的审批日志
     *
     * @param businessKey
     */

    public Object listHistoricTaskInstance(String businessKey) {
        Factory.setOptions(getConfig());
        log.info("获取指定审批流程的审批日志 businessKey {}", businessKey);
        Response response = Factory.History.listHistoricTaskInstance(businessKey, null, TENANT_ID);
        log.info("获取指定审批流程的审批日志 {}", JSON.toJSON(response));
        if (!response.isSuccess()) {
            throw new ActivitiBusinessException(response.getCode(), response.getMessage());
        }
        return response.getData();
    }

    /**
     * 催办指定审批流程节点并发送企业微信消息
     *
     * @param businessKey
     * @param assignee         催办人(操作人)
     * @param assigneeReminder targetAssignee 指定要催办的人，该催办人必须为当前审批节点的审批成员。为空时默认催办节点的所有审批成员
     */
    public Object reminder(String businessKey, String assignee, String assigneeReminder) {
        Factory.setOptions(getConfig());
        log.info("催办指定审批流程节点并发送企业微信消息 businessKey {} assignee {}", businessKey, assignee);
        var response = Factory.Task.reminder(businessKey, assignee, assigneeReminder, TENANT_ID);
        log.info("催办指定审批流程节点并发送企业微信消息 {}", JSON.toJSON(response));
        if (!response.isSuccess()) {
            throw new ActivitiBusinessException(response.getCode(), response.getMessage());
        }
        return response.getData();
    }

    /**
     * 待我审批的
     *
     * @param processDefinitionKey 工作流流程配置id
     * @param assignee             待办人账号
     * @return 流程业务KEY列表
     **/
    public List<String> findWaitApproval(String processDefinitionKey, String assignee) {
        List<String> list = new ArrayList<>();
        Factory.setOptions(getConfig());
        var response = Factory.Task.findWaitApproval(processDefinitionKey, assignee);
//        var response = Factory.Task.listTaskBusinessKeys(assignee, Set.of(processDefinitionKey), Boolean.TRUE);
        if (response.isSuccess()) {
            Object data = response.getData();
            if (data == null) {
                return list;
            }
            if (data instanceof Collection<?>) {
                for (Object s : (Collection<?>) data) {
                    list.add((String) s);
                }
            }
        }
        return list;
    }

    /**
     * 判断用户是否有该流程的审批权限
     *
     * @param businessKey 流程的业务KEY
     * @return 是否有审批权限
     **/
    public boolean hasApproval(String businessKey, String workerEmail) {
        Factory.setOptions(getConfig());
        log.info("businessKey: {}, workerEmail:{}", businessKey, workerEmail);
        var response = Factory.Task.checkFindTaskByCandidateUser(businessKey, workerEmail, TENANT_ID);
        log.info("result: {} ", response.getData());
        if (response.isSuccess()) {
            return (Boolean) response.getData();
        }
        throw new ICException(CommonErrorInfo.unkownError(response.getMessage()));
    }

    /**
     * 获取当前审批节点信息
     *
     * @param businessKey 流程的业务KEY
     * @param workerEmail 审批人邮箱
     * @return 当前审批节点信息
     **/
    public Object findFindTaskNodeInfo(String businessKey, String workerEmail) {
        Factory.setOptions(getConfig());
        log.info("businessKey: {}, workerEmail:{}", businessKey, workerEmail);
        var response = Factory.Task.findTaskByCandidateUser(businessKey, workerEmail, TENANT_ID);
        log.info("result: {} ", response.getData());
        if (!response.isSuccess()) {
            throw new ActivitiBusinessException(response.getCode(), response.getMessage());
        }
        return response.getData();
    }

    /**
     * 获取当前审批节点nodeKey
     *
     * @param businessKey 流程的业务KEY
     * @param workerEmail 审批人邮箱
     * @return nodeKey
     **/
    public String findFindTaskNodeKey(String businessKey, String workerEmail) {
        Object data = this.findFindTaskNodeInfo(businessKey, workerEmail);
        if (data == null) {
            return null;
        }
        List taskNodes = (List) data;
        LinkedHashMap taskNode = (LinkedHashMap) taskNodes.get(0);
        if (taskNode == null) {
            return null;
        }
        if (!taskNode.containsKey("nodeKey")) {
            return null;
        }
        return (String) taskNode.get("nodeKey");
    }

    private Attachment buildAttachment(String label, String labelContent) {
        if (label == null) {
            return null;
        }
        return new Attachment(AttachmentTypeConstant.HTML, label, label, labelContent);
    }

    public String findProcessDefinitionKey(String workFlowConfigId) {
        Optional<String> process = bpmProperties.getProcess(workFlowConfigId.toLowerCase(Locale.ROOT));
        return process.orElseThrow(ExceptionProducer.produceParamException("获取流程配置失败！"));
    }

    private Config getConfig() {
        Config config = new Config();
        config.setGatewayHost(bpmProperties.getGatewayHost());
        return config;
    }

    @Getter
    @AllArgsConstructor
    public enum SendMessageType {
        WECOM("cp_wechat_system"),
        MAIL("mail"),
        SMS("sms"),
        ;

        String code;
    }
}
